//===- lli.cpp - LLVM Interpreter / Dynamic compiler ----------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This utility provides a simple wrapper around the LLVM Execution Engines,
// which allow the direct execution of LLVM programs through a Just-In-Time
// compiler, or through an interpreter if no JIT is available for this platform.
//
//===----------------------------------------------------------------------===//

#include "Emulator.h"
#include "EmulatorIRGenerator.h"
#include <stdio.h>

/// mask used for register reading and writing.
const uint64_t EmulatorIRGenerator::size_mask[6] = {
  0xff,
  0xff00,
  0xffff,                        
  0xffffffff,                  
  0xffffffffffffffff,        
  0xffffffffffffffff      
};

Value* EmulatorIRGenerator::generateReadEntireGPR(uint8_t index)
{
  LLVMContext &Context = getGlobalContext();
  uint32_t offset = getGlobalInterp()->get_reg_offset(index, 0);
  Type  *RegTy = PointerType::getUnqual(Type::getInt64Ty(Context));
  Value *EffAddr = generateGetField(offset); 
  Value *LoadPtr = irBuilder->CreateIntToPtr(EffAddr, RegTy);
  return irBuilder->CreateLoad(LoadPtr); 
}

Value* EmulatorIRGenerator::generateReadEntireSGR(uint8_t index)
{
  LLVMContext &Context = getGlobalContext();
  uint32_t offset = getGlobalInterp()->get_reg_offset(index, 1);
  Type  *RegTy = PointerType::getUnqual(Type::getInt64Ty(Context));
  Value *EffAddr = generateGetField(offset); 
  Value *LoadPtr = irBuilder->CreateIntToPtr(EffAddr, RegTy);
  return irBuilder->CreateLoad(LoadPtr); 
}

Value* EmulatorIRGenerator::generateWriteEntireGPR(uint8_t index, Value *Val)
{
  LLVMContext &Context = getGlobalContext();
  uint32_t offset = getGlobalInterp()->get_reg_offset(index, 0);
  Type  *RegTy = PointerType::getUnqual(Type::getInt64Ty(Context));
  Value *EffAddr = generateGetField(offset); 
  Value *StorePtr = irBuilder->CreateIntToPtr(EffAddr, RegTy);
  return irBuilder->CreateStore(Val, StorePtr); 
}

Value *EmulatorIRGenerator::generateInstruction_GetToXlate(bxInstruction_c *i)
{
  LLVMContext &Context = getGlobalContext();
  Type  *AddrTy = PointerType::getUnqual(Type::getInt64Ty(Context));
  Value *EffAddr = generateGetField(getGlobalInterp()->address_to_xlate_offset);
  Value *LoadPtr = irBuilder->CreateIntToPtr(EffAddr, AddrTy);
  return irBuilder->CreateLoad(LoadPtr); 
}

Value *EmulatorIRGenerator::generateInstruction_GetXlated(bxInstruction_c *i)
{
  LLVMContext &Context = getGlobalContext();
  Type  *AddrTy = PointerType::getUnqual(Type::getInt64Ty(Context));
  Value *EffAddr = generateGetField(getGlobalInterp()->address_xlated_offset);
  Value *LoadPtr = irBuilder->CreateIntToPtr(EffAddr, AddrTy);
  return irBuilder->CreateLoad(LoadPtr); 
}

void EmulatorIRGenerator::generateAsyncCheck(void)
{
  LLVMContext &Context = getGlobalContext();
  uint32_t offset = getGlobalInterp()->get_async_event_offset();
  
  // Get the async_check value.
  Type  *AsyncTy = PointerType::getUnqual(Type::getInt32Ty(Context));
  Value *ZeroVal = ConstantInt::get(Type::getInt32Ty(Context), 0);
  Value *EffAddr = generateGetField(offset);
  Value *LoadPtr = irBuilder->CreateIntToPtr(EffAddr, AsyncTy);
  Value *LoadVal = irBuilder->CreateLoad(LoadPtr); 

  // Check for non-zero.
  Value *CmpVal = irBuilder->CreateICmp(CmpInst::ICMP_EQ, LoadVal, ZeroVal);

  // Create conditional branch. continue or return to runtime.
  BasicBlock *FallBB = 0;
  BasicBlock *TargBB = 0; 
  generateSideExit(FallBB, TargBB);
  irBuilder->CreateCondBr(CmpVal, FallBB, TargBB); 
  // Generate the runtime return IR in the target BB.
  setInsertionPoint(TargBB);
  generateRunTimeRetIR();
  // Set the insertion point to the fall through BB.
  setInsertionPoint(FallBB);
}

void EmulatorIRGenerator::generateInstruction_TLBMissCheck_IR(bxInstruction_c* i)
{
  Value *Xlation = generateInstruction_GetXlated(i);
  Value *ZeroVal = ConstantInt::get(Xlation->getType(), 0);

  // Check for non-zero.
  Value *CmpVal = irBuilder->CreateICmp(CmpInst::ICMP_NE, Xlation, ZeroVal);

  // Create conditional branch. continue or return to runtime.
  BasicBlock *FallBB = 0;
  BasicBlock *TargBB = 0; 
  generateSideExit(FallBB, TargBB);
  irBuilder->CreateCondBr(CmpVal, FallBB, TargBB); 

  // Generate the runtime return IR in the target BB.
  setInsertionPoint(TargBB);

  // Restore RIP.
  Type  *RegTy = PointerType::getUnqual(Type::getInt64Ty(getGlobalContext()));
  Value *EffAddr = generateGetField(getGlobalInterp()->prev_rip_offset);
  Value *LoadPtr = irBuilder->CreateIntToPtr(EffAddr, RegTy);
  generateWrite64GPR(17, irBuilder->CreateLoad(LoadPtr));

  // This is an early exit. Need to force interpretation
  // for the next instruction, as interpreter is the only
  // one that can bring the TLB back.
  generateInstruction_Interpret_IR(i);
   
  // Done. Safely exit.
  generateRunTimeRetIR();

  // Set the insertion point to the fall through BB.
  setInsertionPoint(FallBB);
}

void EmulatorIRGenerator::generateUpdatePrevRIP(bxInstruction_c *i)
{
  LLVMContext &Context = getGlobalContext();
  Type  *RegTy = PointerType::getUnqual(Type::getInt64Ty(Context));
  Value *EffAddr = generateGetField(getGlobalInterp()->prev_rip_offset);
  Value *RIP = generateRead64GPR(17); 
  Value *StorePtr = irBuilder->CreateIntToPtr(EffAddr, RegTy);
  irBuilder->CreateStore(RIP, StorePtr); 
}

void EmulatorIRGenerator::generate_CallOut_IR(bxInstruction_c* i, uint64_t CallAddr, Value* Opaque = 0) 
{
  // Generate callout to interpreter routines.
  LLVMContext &Context = getGlobalContext();

  // Return type is NULL for BochsCall.
  Type *RetTy = Type::getVoidTy(Context); 

  // Get the function address.
  Value *FuncAddr = ConstantInt::get(Type::getInt64Ty(Context), CallAddr);
  
  // Get the argument of the BochsCall.
  Value *ProcAddr = current->arg_begin();
  Value *InstAddr = ConstantInt::get(Type::getInt64Ty(Context), (uint64_t)i); 

  std::vector<Type*>  ArgTys;
  std::vector<Value*> ArgVals;

  ArgTys.push_back(Type::getInt64Ty(Context));
  ArgTys.push_back(Type::getInt64Ty(Context));
  ArgVals.push_back(ProcAddr);
  ArgVals.push_back(InstAddr);

  if (Opaque)
  {
    ArgTys.push_back(Opaque->getType());
    ArgVals.push_back(Opaque);
  }

  // Create the function prototype.
  FunctionType *FuncTy = FunctionType::get(RetTy, ArgTys, 0);
  PointerType  *FuncTyPntr = PointerType::getUnqual(FuncTy);
  Value *FuncCast = irBuilder->CreateIntToPtr(FuncAddr, FuncTyPntr);

  // Create the BochsCall.
  irBuilder->CreateCall(FuncCast, ArgVals);
}

void EmulatorIRGenerator::generateInstruction_Interpret_IR(bxInstruction_c* i)
{
  generate_CallOut_IR(i, (uint64_t) getGlobalInterp()->interpret);
}
void EmulatorIRGenerator::generateInstruction_Naked_Interpret_IR(bxInstruction_c* i)
{
  generate_CallOut_IR(i, (uint64_t) getGlobalInterp()->naked_interpret);
}
void EmulatorIRGenerator::generateInstruction_GetResolveModRM_IR(bxInstruction_c* i)
{
  generate_CallOut_IR(i, (uint64_t) getGlobalInterp()->resolve_modrm);
}
void EmulatorIRGenerator::generateInstruction_GetModRMXlation_IR(bxInstruction_c* i)
{
  generate_CallOut_IR(i, (uint64_t) getGlobalInterp()->modrm_xlation);
}

void EmulatorIRGenerator::generateRunTimeRetIR()
{
  // Generate callout to interpreter routines.
  LLVMContext &Context = getGlobalContext();

  // Create the address to jump to.
  uint64_t RTAddr = (uint64_t)getGlobalCodeGen()->getJITExitRoutine();
  Type  *RTTy = PointerType::getUnqual(Type::getInt64Ty(Context));
  Value *Addr = ConstantInt::get(Type::getInt64Ty(Context), RTAddr);
  Value *JumpPtr = irBuilder->CreateIntToPtr(Addr, RTTy);

  irBuilder->CreateIndirectBr(JumpPtr);
}

void EmulatorIRGenerator::generateInstruction_INC_ERX_IR(bxInstruction_c* i)
{
  if (!i->ResolveModrm)
  {
    // Update the RIP.
    generateUpdateRIP(i);

    Value *XRX = generateReadGPR(i->rm(), i); 
    Value *AddVal = irBuilder->CreateAdd(XRX, ConstantInt::get(XRX->getType(), 1));
    generateWriteGPR(i->rm(), AddVal, i);

    // Update the prev_RIP.
    generateUpdatePrevRIP(i); 

    // Truly emulated 1 instruction in JIT.
    generateUpdateInstCount(i);
  }
  else 
  {
    generateInstruction_Interpret_IR(i);
  }
}

void EmulatorIRGenerator::generateInstruction_XCHG_XAX_IR(bxInstruction_c* i)
{
  if (!i->ResolveModrm)
  {
    // Update the RIP.
    generateUpdateRIP(i);

    // Generate xchg registers.
    Value *XAX = generateReadGPR(0, i);
    Value *RRM = generateReadGPR(i->rm(), i);
    generateWriteGPR(i->rm(), XAX, i);
    generateWriteGPR(0, RRM, i);

    // Update the prev_RIP.
    generateUpdatePrevRIP(i); 

    // Truly emulated 1 instruction in JIT.
    generateUpdateInstCount(i);
  }
  else 
  {
    generateInstruction_Interpret_IR(i);
  }
}

void EmulatorIRGenerator::generateInstruction_MOV_ERXIv_IR(bxInstruction_c* i)
{
  // Update the RIP.
  generateUpdateRIP(i);

  // Extract the immediate value and write it to the register.
  Value *IV = generateGetInstructionIv(i);
  generateWriteGPR(i->rm(), IV, i);

  // Update the prev_RIP.
  generateUpdatePrevRIP(i); 

  // Truly emulated 1 instruction in JIT.
  generateUpdateInstCount(i);
}

void EmulatorIRGenerator::generateInstruction_MOV_ERXIb_IR(bxInstruction_c* i)
{
}

void EmulatorIRGenerator::generateInstruction_MOV_G8E8R_IR(bxInstruction_c* i)
{
}


void EmulatorIRGenerator::generateInstruction_MOV_GxExM_IR(bxInstruction_c* i)
{
  if (!i->ResolveModrm)
  {
    // Update the RIP.
    generateUpdateRIP(i);

    Value *RRM = generateReadGPR(i->rm(), i);
    generateWriteGPR(i->nnn(), RRM, i);

    // Update the prev_RIP.
    generateUpdatePrevRIP(i); 
  }
  else 
  {
    // Update the RIP.
    generateUpdateRIP(i);

    // Get the translated address.
    generateInstruction_GetResolveModRM_IR(i);
    generateInstruction_GetModRMXlation_IR(i);

    // TLB miss happened ?
    generateInstruction_TLBMissCheck_IR(i);

    // Load from the translated address.
    Value *MemVal = generateInstruction_FetchXlated(i);
    generateWriteGPR(i->nnn(), MemVal, i);

    // generateInstruction_Naked_Interpret_IR(i);

    // Update the prev_RIP.
    generateUpdatePrevRIP(i); 

    // Truly emulated 1 instruction in JIT.
    generateUpdateInstCount(i);
  }
}

void EmulatorIRGenerator::generateInstruction_MOV_GxExR_IR(bxInstruction_c* i)
{
  if (!i->ResolveModrm)
  {
    /// Move register to register.

    // Update the RIP.
    generateUpdateRIP(i);

    Value *RRM = generateReadGPR(i->rm(), i);
    generateWriteGPR(i->nnn(), RRM, i);

    // Update the prev_RIP.
    generateUpdatePrevRIP(i); 

    // Truly emulated 1 instruction in JIT.
    generateUpdateInstCount(i);
  }
  else 
  {
#if 0
    Value *LAddr = generateInstruction_GetLinAddress(i);
    generate_CallOut_IR(i, (uint64_t) getGlobalInterp()->naked_interpret, LAddr);

    Value *SAddr =  
    Value *EAddr = irBuilder->CreateAddr(LAddr, SAddr); 
#endif
#if 0
    // Update the RIP.
    generateUpdateRIP(i);

    /// Move register to memory.
    LLVMContext &Context = getGlobalContext();

    Value *RAddr = generateGetLinAddress(i);
    Value *VAddr = generateGetVirtAddress(i);
    Value *TAddr = generateTLBWriteTranslate(i, Addr);
    Value *RRM = generateReadGPR(i->rm(), i);
    Type  *StoreTy = PointerType::getUnqual(Type::getInt64Ty(Context));
    Value *StorePtr = irBuilder->CreateIntToPtr(TAddr, StoreTy);
    Value *LV = irBuilder->CreateStore(RRM, StorePtr); 

    // Update the prev_RIP.
    generateUpdatePrevRIP(i); 
#endif
    generateInstruction_Interpret_IR(i);
  }
}

void EmulatorIRGenerator::generateInstructionIR(bxInstruction_c* i)
{
  uint16_t ia_opcode = i->metaInfo.opcode;
  
  // Let the fun begin.
  switch(ia_opcode)
  {
#if 0
    case 0x40 ... 0x47:
      CRACK_INS("INC_ERX");
      generateInstruction_INC_ERX_IR(i);
      break;
#endif
    case 0x88:
      CRACK_INS("MOV_G8E8R");
      generateInstruction_Interpret_IR(i);
      break;
    case 0x89:
      CRACK_INS("MOV_GxExR");
      generateInstruction_MOV_GxExR_IR(i);
      break;
    case 0x8b:
      CRACK_INS("MOV_GxExM");
      generateInstruction_MOV_GxExM_IR(i);
      break;
#if 1 
    case 0x91 ... 0x97:
      CRACK_INS("XCHG_XAX");
      generateInstruction_XCHG_XAX_IR(i);
      break;
    case 0xb0 ... 0xb7:
      CRACK_INS("MOV_ERXIb");
      generateInstruction_Interpret_IR(i);
      break;
    case 0xb8 ... 0xbf:
      CRACK_INS("MOV_ERXIv");
      generateInstruction_MOV_ERXIv_IR(i);
      break;
#endif
    default:
     // Generate every instruction into callouts.
     generateInstruction_Interpret_IR(i);
     break;
  }
  // Check for asynchronous events.
  generateAsyncCheck();
}


